import sharp from 'sharp';
import { readFile } from 'fs/promises';
import path from 'path';
import { createHash } from 'crypto';

/**
 * Sharp Webpack Loader
 * 直接在构建过程中处理图像资源
 */
export default function sharpLoader(source) {
  // 告诉 webpack/rspack 我们需要接收原始的二进制数据（Buffer）
  this.raw = true;
  
  const callback = this.async();
  const options = this.getOptions() || {};
  const { sharpOptions } = options;

  // 如果没有配置选项，直接返回原始内容
  if (!sharpOptions) {
    return callback(null, source);
  }

  const resourcePath = this.resourcePath;
  
  // 检查文件大小过滤
  const minSize = sharpOptions.filter?.minSize || 512;
  if (source.length < minSize) {
    if (sharpOptions.debug) {
      console.log(`Skipping ${resourcePath} (too small: ${source.length} bytes, min: ${minSize})`);
    }
    return callback(null, source);
  }

  // 规范化输入，确保是 Buffer
  const ensureBuffer = async (input) => {
    if (Buffer.isBuffer(input)) return input;
    if (typeof input === 'string') {
      // 尝试从磁盘读取原文件，避免字符串导致二进制损坏
      try {
        return await readFile(resourcePath);
      } catch {
        // 回退：尽力而为的转换（可能无压缩收益）
        return Buffer.from(input, 'binary');
      }
    }
    // Uint8Array 等情况
    return Buffer.from(input);
  };

  (async () => {
    try {
      const inputBuffer = await ensureBuffer(source);
      // 异步处理图像
      const processedBuffer = await processImageDirect(inputBuffer, sharpOptions, resourcePath, this);
      // 添加依赖，确保文件变化时重新构建
      this.addDependency(resourcePath);
      
      if (sharpOptions.debug) {
        console.log(`Processed ${resourcePath}: ${inputBuffer.length} -> ${processedBuffer.length} bytes`);
      }
      
      // 返回处理后的图像数据
      callback(null, processedBuffer);
    } catch (error) {
      console.error(`Sharp loader error for ${resourcePath}:`, error);
      callback(null, source); // 出错时返回原始内容
    }
  })();
}

/**
 * 直接处理图像数据
 */
async function processImageDirect(inputBuffer, options, resourcePath, loaderCtx) {
  try {
    // 创建 Sharp 实例
    const sharpInstance = sharp(inputBuffer);
    const metadata = await sharpInstance.metadata();
    
    if (options.debug) {
      console.log(`Processing ${resourcePath}: ${metadata.format} ${metadata.width}x${metadata.height}`);
    }

    // 根据原始格式进行压缩，使用优化的设置
    let processedBuffer;
    
    switch (metadata.format) {
      case 'jpeg':
        processedBuffer = await sharpInstance
          .jpeg({
            quality: options.compress?.jpeg?.quality || options.quality?.jpeg || 82,
            progressive: options.compress?.jpeg?.progressive !== false,
            mozjpeg: options.compress?.jpeg?.mozjpeg !== false,
            optimiseScans: options.compress?.jpeg?.optimiseScans !== false,
            optimiseCoding: options.compress?.jpeg?.optimiseCoding !== false
          })
          .toBuffer();
        break;

      case 'png':
        // 对于 PNG，优先启用调色板量化（除非显式禁用）
        const pngBase = {
          quality: options.compress?.png?.quality || options.quality?.png || 85,
          compressionLevel: options.compress?.png?.compressionLevel || 9,
          adaptiveFiltering: options.compress?.png?.adaptiveFiltering !== false,
        };
        const paletteExplicit = options.compress?.png?.palette;
        const paletteDefault = true; // 默认开启量化以获得更明显体积收益
        const palette1 = paletteExplicit !== undefined ? paletteExplicit : paletteDefault;

        // 尝试方案 A（使用新的实例，避免复用同一 pipeline）
        let pngOptsA = { ...pngBase, palette: palette1 };
        processedBuffer = await sharp(inputBuffer).png(pngOptsA).toBuffer();

        // 若无收益，再尝试方案 B（切换 palette）
        if (processedBuffer.length >= inputBuffer.length) {
          const palette2 = !palette1;
          const pngOptsB = { ...pngBase, palette: palette2 };
          try {
            const altBuf = await sharp(inputBuffer).png(pngOptsB).toBuffer();
            if (altBuf.length < processedBuffer.length) {
              processedBuffer = altBuf;
              if (options.debug) {
                console.log(`PNG alt strategy improved (${palette2 ? 'palette:on' : 'palette:off'}): ${resourcePath}`);
              }
            }
          } catch {}
        }
        break;

      case 'webp':
        processedBuffer = await sharpInstance
          .webp({
            quality: options.compress?.webp?.quality || options.quality?.webp || 78,
            effort: options.compress?.webp?.effort || 6,
            lossless: options.compress?.webp?.lossless || false,
            nearLossless: options.compress?.webp?.nearLossless || false,
            smartSubsample: options.compress?.webp?.smartSubsample !== false
          })
          .toBuffer();
        break;

      default:
        // 对于不支持的格式，保持原样
        processedBuffer = await sharpInstance.toBuffer();
        break;
    }

    // 智能回退机制：检查压缩效果（门槛可配置）
    const compressionRatio = (inputBuffer.length - processedBuffer.length) / inputBuffer.length;
    const minCompressionThreshold = typeof options.fallback?.minCompressionRatio === 'number'
      ? options.fallback.minCompressionRatio
      : 0.02; // 默认 2%
    
    if (processedBuffer.length >= inputBuffer.length || compressionRatio < minCompressionThreshold) {
      if (options.debug) {
        const change = processedBuffer.length - inputBuffer.length;
        const changePercent = (change / inputBuffer.length * 100).toFixed(2);
        console.log(`Keep original (insufficient compression): ${resourcePath}`);
        console.log(`  Size change: ${inputBuffer.length} -> ${processedBuffer.length} bytes (${changePercent > 0 ? '+' : ''}${changePercent}%)`);
      }
      return inputBuffer;
    }

    if (options.debug) {
      const savings = (compressionRatio * 100).toFixed(2);
      console.log(`Compressed successfully: ${resourcePath}`);
      console.log(`  Size: ${inputBuffer.length} -> ${processedBuffer.length} bytes (-${savings}%)`);
    }

    // 可选：格式转换（两种模式）
    // - sidecar（默认）：额外输出 WebP/AVIF 旁路文件，不改写原始引用
    // - replace：若转换收益达标，则直接用转换后的 Buffer 作为主资产返回（不再发射旁路与原始压缩结果）
    try {
      // 解析目标转换格式：支持新旧两种写法
      let convert;
      if (typeof options.convert === 'string') {
        convert = options.convert; // 'webp' | 'avif'
      } else if (options.convert && typeof options.convert === 'object') {
        convert = options.convert.toWebP ? 'webp' : options.convert.toAVIF ? 'avif' : undefined;
      }

      // 新增 deleteOriginal 布尔开关：true 等价于 replace，false 等价于 sidecar
      // 若显式提供 convertMode，则以 convertMode 为准，兼容旧配置
      const inferredMode = options.deleteOriginal ? 'replace' : 'sidecar';
      const convertMode = options.convertMode || inferredMode; // 'sidecar' | 'replace'

      // 允许的来源格式，默认 ['jpeg','jpg','png']
      const allowedFrom = Array.isArray(options.convertFrom) && options.convertFrom.length
        ? options.convertFrom
        : ['jpeg', 'jpg', 'png'];

      const enableConvert = convert === 'webp' || convert === 'avif';
      const isConvertible = allowedFrom.includes(metadata.format);
      if (enableConvert && isConvertible) {
        const convInstance = sharp(inputBuffer);
        let convBuf;
        if (convert === 'webp') {
          const webpOpts = {
            quality: options.compress?.webp?.quality ?? options.quality?.webp ?? 78,
            effort: options.compress?.webp?.effort ?? 6,
            lossless: options.compress?.webp?.lossless ?? false,
            nearLossless: options.compress?.webp?.nearLossless ?? false,
            smartSubsample: options.compress?.webp?.smartSubsample !== false
          };
          convBuf = await convInstance.webp(webpOpts).toBuffer();
        } else {
          const avifOpts = {
            quality: options.compress?.avif?.quality ?? options.quality?.avif ?? 60,
            effort: options.compress?.avif?.effort ?? 6
          };
          convBuf = await convInstance.avif(avifOpts).toBuffer();
        }

        // 以“最佳基准”进行比较（原图 vs 压缩后）
        const baseBuf = processedBuffer.length < inputBuffer.length ? processedBuffer : inputBuffer;
        const convRatioBase = (baseBuf.length - convBuf.length) / baseBuf.length;
        const convMin = typeof options.fallback?.minCompressionRatio === 'number' ? options.fallback.minCompressionRatio : 0.02;
        const convertPolicy = options.convertPolicy === 'always' ? 'always' : 'smaller';

        const shouldReplace = convertMode === 'replace' && (
          convertPolicy === 'always' || (convBuf.length < baseBuf.length && convRatioBase >= convMin)
        );

        if (shouldReplace) {
          if (options.debug) {
            const pct = (convRatioBase * 100).toFixed(2);
            const note = convertPolicy === 'always' ? ' (policy: always)' : '';
            console.log(`Replaced with ${convert.toUpperCase()} (-${pct}%) for ${resourcePath}${note}`);
          }
          return convBuf; // 直接作为主资产返回（原图或压缩后均被替换）
        }

        // sidecar：仅在更小且达到阈值时发射旁路（避免无意义体积膨胀）
        if (loaderCtx && typeof loaderCtx.emitFile === 'function' && convBuf.length < baseBuf.length && convRatioBase >= convMin) {
          // sidecar
          const origDir = path.dirname(resourcePath);
          const origBase = path.basename(resourcePath);
          const origName = origBase.replace(/\.[^.]+$/, '');
          const hash = createHash('sha1').update(convBuf).digest('hex').slice(0, 8);
          const ext = convert === 'webp' ? '.webp' : '.avif';
          const fileName = `${origName}.${hash}${ext}`;
          // 相对输出目录的子路径：尽量保持到 image 子目录
          const lower = origDir.toLowerCase();
          let outDir = 'static/image';
          const idx = lower.lastIndexOf('image');
          if (idx !== -1) {
            const rel = resourcePath.slice(origDir.slice(0, idx).length + 1);
            const relDir = path.dirname(rel);
            outDir = path.posix.join('static', relDir.replace(/\\/g, '/'));
          }
          const outPath = path.posix.join(outDir, fileName);
          loaderCtx.emitFile(outPath, convBuf);
          if (options.debug) {
            const pct = (convRatioBase * 100).toFixed(2);
            console.log(`Emitted sidecar ${convert.toUpperCase()}: ${outPath} (-${pct}%) for ${resourcePath}`);
          }
        } else if (options.debug) {
          const pct = (convRatioBase * 100).toFixed(2);
          console.log(`Skip convert ${convert.toUpperCase()} (no benefit ${pct}%): ${resourcePath}`);
        }
      }
    } catch (e) {
      if (options.debug) {
        console.log('Sidecar convert emit failed:', e?.message || e);
      }
    }

    return processedBuffer;
  } catch (error) {
    console.error(`Error processing image:`, error);
    throw error;
  }
}

// 处理二进制数据
sharpLoader.raw = true;
